package com.jetpackframework.virtualapk;

import android.app.Application;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.os.Parcelable;

import androidx.annotation.Nullable;

import com.jetpackframework.Reflector;
import com.jetpackframework.virtual.VirtualApk;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class RemoteService extends Service {
    public static final int EXTRA_COMMAND_START_SERVICE = 66;
    public static final int EXTRA_COMMAND_BIND_SERVICE = 77;
    public static final int EXTRA_COMMAND_STOP_SERVICE = 88;
    public static final int EXTRA_COMMAND_UNBIND_SERVICE = 99;
    public static final String EXTRA_COMMAND = "EXTRA_COMMAND";
    public static final String EXTRA_INTENT = "EXTRA_INTENT";

    private VirtualInstaller virtualInstaller;
    private Map<ComponentName,Service> serviceMap;

    @Override
    public void onCreate() {
        super.onCreate();
        virtualInstaller = VirtualInstaller.getInstance();
        serviceMap = new HashMap<>();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int command = intent.getIntExtra(EXTRA_COMMAND,0);
        switch (command){
            case EXTRA_COMMAND_START_SERVICE:
                startService(intent.getParcelableExtra(EXTRA_INTENT), flags, startId);
                break;
            case EXTRA_COMMAND_BIND_SERVICE:
                bindService(intent.getParcelableExtra(EXTRA_INTENT), flags, startId);
                break;
            case EXTRA_COMMAND_STOP_SERVICE:
                stopService(intent.getParcelableExtra(EXTRA_INTENT), flags, startId);
                break;
            case EXTRA_COMMAND_UNBIND_SERVICE:
                unbindService(intent.getParcelableExtra(EXTRA_INTENT), flags, startId);
                break;
        }
        return super.onStartCommand(intent, flags, startId);
    }

    private void unbindService(Intent intent, int flags, int startId) {
        Service service = serviceMap.get(intent.getComponent());
        if (service != null){
            service.onUnbind(intent);
            service.onDestroy();
            serviceMap.remove(intent.getComponent());
        }
    }

    private void stopService(Intent intent, int flags, int startId) {
        Service service = serviceMap.get(intent.getComponent());
        if (service != null){
            service.onDestroy();
            serviceMap.remove(intent.getComponent());
        }
    }

    private void startService(Intent intent, int flags, int startId) {
        VirtualApk virtualApk = getVirtualApk(intent);
        if (virtualApk != null){
            try {
                Service service = (Service) virtualApk.getClassLoader().loadClass(intent.getComponent().getClassName()).newInstance();
                execServiceAttach(service,intent.getComponent());
                service.onCreate();
                service.onStartCommand(intent,flags,startId);
                serviceMap.put(intent.getComponent(),service);
            } catch (IllegalAccessException | InstantiationException | ClassNotFoundException | NoSuchMethodException | Reflector.ReflectedException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    private void bindService(Intent intent, int flags, int startId){
        VirtualApk virtualApk = getVirtualApk(intent);
        if (virtualApk != null) {
            try {
                Service service = (Service) virtualApk.getClassLoader().loadClass(intent.getComponent().getClassName()).newInstance();
                execServiceAttach(service,intent.getComponent());
                service.onCreate();
                IBinder iBinder = service.onBind(intent);
                IBinder serviceConn = intent.getExtras().getBinder("sc");
                Object iServiceConn = Reflector.on("android.app.IServiceConnection$Stub").method("asInterface", IBinder.class).call(serviceConn);
                if (Build.VERSION.SDK_INT >= 26){
                    Reflector.QuietReflector.with(iServiceConn).method("connected", ComponentName.class, IBinder.class,boolean.class).call(intent.getComponent(), iBinder,false);
                }else {
                    Reflector.QuietReflector.with(iServiceConn).method("connected", ComponentName.class, IBinder.class).call(intent.getComponent(), iBinder);
                }
                serviceMap.put(intent.getComponent(),service);
                virtualInstaller.putServiceConnection(serviceConn,intent);
            } catch (IllegalAccessException | InstantiationException | ClassNotFoundException | NoSuchMethodException | Reflector.ReflectedException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    private void execServiceAttach(Service service, ComponentName component) throws ClassNotFoundException, NoSuchMethodException, Reflector.ReflectedException, InvocationTargetException, IllegalAccessException {
        Object mainThread = Reflector.on("android.app.ActivityThread").method("currentActivityThread").call();
        Object appThread = Reflector.with(mainThread).method("getApplicationThread").call();
        IBinder token = Reflector.with(appThread).method("asBinder").call();

        Method attach = service.getClass().getMethod("attach", Context.class, Class.forName("android.app.ActivityThread"), String.class, IBinder.class, Application.class, Object.class);
        attach.invoke(service, getApplicationContext(), mainThread, component.getClassName(), token, getApplication(), virtualInstaller.getActivityManager());

    }

    private VirtualApk getVirtualApk(Intent intent) {
        ComponentName component = intent.getComponent();
        VirtualApk virtualApk = virtualInstaller.getVirtualApk(component.getPackageName());
        return virtualApk;
    }
}
